home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 April: Mac OS SDK / Dev.CD Apr 98 SDK2.toast / Development Kits (Disc 2) / ScriptX / Code Samples / tapemesr / source / tape.sx < prev    next >
Encoding:
Text File  |  1996-05-21  |  15.5 KB  |  462 lines  |  [TEXT/ttxt]

  1. --<<<-
  2. -- Filename: 
  3. --     tape.sx
  4. --
  5. -- Other Files Required:
  6. --     interfac.sxl - defines accessory interface module (interfac.sx)
  7. --
  8. -- Purpose:  
  9. --     tape.sx defines the classes for the tape measure tool.
  10. --
  11. -- Specialized Classes:  
  12. --     Hook, TextDisplay, TapeMeasure
  13. --
  14. -- Instructions to User: 
  15. --     The Hook class is a draggable TwoDMultiPresenter which works together 
  16. --     with the TapeMeasure to realize the tape measure object. The Hook part 
  17. --     can be dragged around, with one end anchored at its homeSpace. The 
  18. --     xSetter and ySetter methods of the Hook are overridden to rubberband a
  19. --     line from the Hook to its homeSpace, and notify the homeSpace when it 
  20. --     has moved. The TapeMeasure is a draggable GroupSpace which manages the 
  21. --     Hook and the TextDisplay.
  22. --
  23. -- Author:
  24. --     Steve Mayer
  25. --     Robert Lockstone : 12-15-95 : Convert to 1.5 and modularize
  26.  
  27. --*=============================================================================*
  28. --* Use the interface module defined in AutoFinder.
  29. --*=============================================================================*
  30. global autofindDir := spawn (parentDir (parentDir theScriptDir)) "autofind"
  31.  
  32. --*=============================================================================*
  33. --* Open the library which defines the Accessory Interface module used by 
  34. --* AutoFinder.
  35. --*=============================================================================*
  36. if ((getModule @AccessoryInterface) = false) do 
  37.    (
  38.    open LibraryContainer dir:autofindDir     \
  39.                          path:"interfac.sxl" \
  40.                          mode:@readable
  41.    )
  42.  
  43. --*=============================================================================*
  44. --* Define the interface for these Tape Measure classes.  Make sure to use
  45. --* the accessory interface here.
  46. --*=============================================================================*
  47. module TapeInterface
  48.    uses AccessoryInterface with exports everything end
  49.  
  50.    exports Hook
  51.    exports instance variables homeSpace, dc, hookLine, hookInterpolator, display
  52.    
  53.    exports TextDisplay
  54.    exports instance variables textObject
  55.    exports dimensionSetter
  56.    
  57.    exports TapeMeasure
  58.    exports instance variables display, hook, foundHome
  59.    exports formatValue, updateDisplay, updateScale
  60. end
  61.  
  62. --*=============================================================================*
  63. --* Define the implementation module.
  64. --*=============================================================================*
  65. module TapeImplementation
  66.    uses ScriptX
  67.    uses TapeInterface with exports everything end
  68. end
  69.  
  70. in module TapeImplementation
  71.  
  72. class Hook (TwoDMultiPresenter, Dragger)
  73. instance variables
  74.     homeSpace
  75.     dc
  76.     hookLine
  77.     hookInterpolator
  78.     display
  79. end
  80.  
  81. method init self {class Hook} #rest args \
  82.                               #key hub: ->
  83.    (
  84.    apply nextMethod self args
  85.     
  86.    prepend hub self
  87.  
  88.    local center := new Point x:(hub.width / 2) y:(hub.height / 2)
  89.    local l := new TwoDShape boundary:(new Line x2:-30 y2:-30) \
  90.                              stroke:blackBrush
  91.    translate l.transform center.x center.y
  92.    prepend hub l
  93.    self.hookLine := l
  94.  
  95.    --*==========================================================================*
  96.    --* Create a DragController so I can be grabbed and dragged around.  Set
  97.    --* wholeSpace to true to avoid having to reappend myself to this controller
  98.    --* in the drop method.  There is a bug which prevents that reappending.
  99.    --* Ideally, wholeSpace should be false.
  100.    --*==========================================================================*    
  101.    self.dc := new DragController space:hub wholeSpace:true
  102.     
  103.    return self
  104.    )
  105.  
  106. --*=============================================================================*
  107. --* Grab method is called when the hook gets a MouseDownEvent.
  108. --*=============================================================================*
  109. method grab self {class Hook} grabPoint ->
  110.    (
  111.    local p  := self.presentedBy
  112.    local pp := p.presentedBy
  113.  
  114.    self.homeSpace := p
  115.     
  116.    -- Remove self and line from home space.
  117.    deleteOne p self
  118.    deleteOne p self.hookLine
  119.     
  120.    -- Translate self and line by parent space's (x,y)
  121.    translate self.transform p.x p.y
  122.    translate self.hookLine.transform p.x p.y
  123.     
  124.    -- Add self and line to parent space.
  125.    prepend pp self.hookLine
  126.    prepend pp self
  127.  
  128.    --*==========================================================================*
  129.    --* Find a DragController in the parent's space (there must be one because
  130.    --* 'addToTitle' will create one if there isn't one there).  If the
  131.    --* DragController's wholeSpace is false, then append myself to that
  132.    --* DragController so the hook can be dragged independently of the hub.
  133.    --*==========================================================================*
  134.    local dc := chooseOne pp.controllers \
  135.                   (
  136.                   control xx ->
  137.                      (
  138.                      if (isAKindOf control DragController) then return true
  139.                                                            else return false
  140.                      )
  141.                   ) undefined
  142.     
  143.    if (NOT dc.wholeSpace) do append dc self
  144.    )
  145.  
  146. method xSetter self {class Hook} value ->
  147. (
  148.     nextMethod self value
  149.     local b := self.hookLine.boundary
  150.     if (self.homeSpace = self.presentedBy) then
  151.     (
  152.         b.x2 := value - 30
  153.     ) else
  154.     (
  155.         b.x2 := value - self.homeSpace.x - 30
  156.     )
  157.     self.hookLine.changed := true
  158.     updateDisplay self.homeSpace b
  159.     return value
  160. )
  161.  
  162. method ySetter self {class Hook} value ->
  163. (
  164.     nextMethod self value
  165.     local b := self.hookLine.boundary
  166.     if (self.homeSpace = self.presentedBy) then
  167.     (
  168.         b.y2 := value - 30
  169.     ) else
  170.     (
  171.         b.y2 := value - self.homeSpace.y - 30
  172.     )
  173.     self.hookLine.changed := true
  174.     updateDisplay self.homeSpace b
  175.     return value
  176. )
  177.  
  178. --*=============================================================================*
  179. --* Drop method is called when the hook gets a MouseUpEvent.
  180. --*=============================================================================*
  181. method drop self {class Hook} dropPoint ->
  182. (
  183.     -- Delete self and line from the parent space.
  184.     deleteOne self.presentedBy self.hookLine
  185.     deleteOne self.presentedBy self
  186.     
  187.     -- Translate self and line back to home space coordinates.
  188.     translate self.transform (- self.homeSpace.x) (- self.homeSpace.y)
  189.     translate self.hookLine.transform (- self.homeSpace.x) (- self.homeSpace.y)
  190.  
  191.     -- Add self and line back to home space.
  192.     prepend self.homeSpace self.hookLine
  193.     prepend self.homeSpace self
  194.  
  195.     self.homeSpace.changed := true
  196. )
  197.  
  198. -- Class TextDisplay is a draggable display for a text presenter. It
  199. -- looks in the media table under "background" for the background bitmap.
  200. class TextDisplay (Dragger, GroupSpace)
  201. instance variables
  202.     textObject
  203. end
  204.  
  205. method init self {class TextDisplay} #rest args \
  206.                                      #key media: ->
  207. (
  208.     apply nextMethod self args
  209.  
  210.     append self media[@background]
  211.     
  212.     -- Create text presenter for display.
  213.     local t := new TextPresenter boundary:media[@background].boundary.bBox \
  214.         target:("" as Text)
  215.     setDefaultAttr t @alignment @center
  216.     setDefaultAttr t @font (new PlatformFont macintoshName:"Palatino" \
  217.                                              windowsName:"Times")
  218.     setDefaultAttr t @size 14
  219.     setDefaultAttr t @leading 16
  220.     setDefaultAttr t @weight @heavy
  221.     prepend self t
  222.     self.textObject := t
  223.     self.dimension  := 1
  224.     return self
  225. )
  226.  
  227. -- Method dimensionSetter sets the y offset based on 1 or 2 dimensions.
  228. method dimensionSetter self {class TextDisplay} value ->
  229. (
  230.     if value = 1 then
  231.         self.textObject.y := 14
  232.     else
  233.         self.textObject.y := 7
  234. )
  235.  
  236. -- Method valueSetter sets the text of the text display.
  237. method valueSetter self {class TextDisplay} value ->
  238. (
  239.     self.textObject.target := (value as Text)
  240. )
  241.  
  242. -- Class TapeMeasure implements a draggable tool used to measure two
  243. -- dimensional space in one or two dimensions. It uses an instance of
  244. -- the hook class to provide a draggable end point for the tape.
  245. class TapeMeasure (Dragger, GroupSpace)
  246. instance variables
  247.     display
  248.     hook
  249.     scale
  250.     foundHome:false    -- True if we're really displayed
  251. end
  252.  
  253. method init self {class TapeMeasure} #rest args                                 \
  254.                                      #key media:                                \
  255.                                           display:                              \
  256.                                           fill:(new Brush color:(new RGBColor   \
  257.                                                                      red:100    \
  258.                                                                      green:200  \
  259.                                                                      blue:200)) \
  260.                                           stroke:blackBrush ->
  261. (
  262.     apply nextMethod self args
  263.  
  264.     self.display := display
  265.     
  266.     -- Default scale is 1 dimension, 1 pixel per pixel.
  267.     self.scale := #(#("pixels", 1))
  268.  
  269.     return self
  270. )
  271.  
  272. method afterInit self {class TapeMeasure}                                    \
  273.        #rest args                                                            \
  274.        #key media:                                                           \
  275.             display:                                                         \
  276.             fill:(new Brush color:(new RGBColor red:100 green:200 blue:200)) \
  277.             stroke:blackBrush ->
  278. (
  279.     apply nextMethod self args
  280.  
  281.     prepend self media[@ring]
  282.     prepend self media[@hub]
  283.     
  284.     local h := new Hook boundary:(media[@hook].boundary.bBox) hub:self
  285.     prepend h media[@hook]
  286.     self.hook := h
  287.     updateDisplay self h.hookLine.boundary
  288.     
  289.     return self
  290. )
  291.  
  292. -- Method updateScale updates the scale of the tape measure.
  293. method updateScale self {class TapeMeasure} ->
  294. (
  295.     -- Get space's scale and update the display
  296.     if ((self.presentedby <> undefined) and 
  297.         (isDefined getScale) and
  298.         (canObjectDo self.presentedby getScale)) do
  299.     (
  300.         self.scale:= getScale self.presentedby
  301.     )
  302.     self.display.dimension := size self.scale
  303.     updateDisplay self self.hook.hookLine.boundary
  304. )
  305.  
  306. method formatValue self {class TapeMeasure} value ->
  307. (
  308.     local x := value as String
  309.     local i := getKeyOne x "."[1]
  310.     if (i <> empty) do
  311.         deleteFromTo x (i + 2) (size x)
  312.     return x
  313. )
  314.  
  315. -- Method updateDisplay updates the current measurement readout.
  316. method updateDisplay self {class TapeMeasure} aLine ->
  317. (
  318.     local displayString
  319.     local deltaX := abs (aLine.x2 - aLine.x1)
  320.     local deltaY := abs (aLine.y2 - aLine.y1)
  321.     -- If measuring in 1 dimension, use the distance formula.
  322.     if ((size self.scale) = 1) then
  323.     (
  324.         local distance := (sqrt ((deltaX * deltaX) + (deltaY * deltaY))) / self.scale[1][2]
  325.         displayString := (formatValue self distance) + " " + \
  326.             self.scale[1][1]
  327.     )
  328.     -- If measuring in 2 dimensions, use the delta x and y.
  329.     else
  330.     (
  331.         deltaX := deltaX / self.scale[1][2]
  332.         deltaY := deltaY / self.scale[2][2]
  333.         displayString := (formatValue self deltaX) + " " + \
  334.             self.scale[1][1] + "\r" + (formatValue self deltaY) + " " + \
  335.             self.scale[2][1]
  336.     )
  337.     self.display.value := displayString
  338. )
  339.  
  340. --*=============================================================================*
  341. --* Adds a TapeMeasure to a running title.
  342. --*=============================================================================*
  343. method addToTitle self {class TapeMeasure} title #key pres:(undefined) ->
  344.    (
  345.    if (self.foundHome) do return  --Only display self in one place
  346.  
  347.    --*==========================================================================*
  348.    --* If one isn't supplied, try to find a presenter which can perform the
  349.    --* getScale method.
  350.    --*==========================================================================*
  351.    if (isDefined getScale) and (pres = undefined) do
  352.       (
  353.       for w in title.windows until pres <> undefined do
  354.          (
  355.          if (canObjectDo w getScale) then pres := w
  356.          else
  357.             (
  358.             for p in w until pres <> undefined do
  359.                (
  360.                if (canObjectDo p getScale) do pres := p
  361.                )
  362.             )
  363.          )
  364.       )
  365.  
  366.    --*==========================================================================*
  367.    --* See if 'currentScene' IV is available and use it if it is.  If all else 
  368.    --* fails, default to the topmost window of the title.  Scale will default
  369.    --* to measuring in pixels.
  370.    --*==========================================================================*   
  371.    if (pres = undefined)             and \
  372.       (isDefined currentSceneGetter) and \
  373.       (canObjectDo title currentSceneGetter) then
  374.       (
  375.       pres := title.currentScene
  376.       )
  377.    else
  378.       (
  379.       if (pres = undefined) do pres := title.windows[1]
  380.       )
  381.  
  382.    --*==========================================================================*
  383.    --* Actually add the tape measure display and the tape measure to the window.
  384.    --*==========================================================================*
  385.    prepend pres self.display
  386.    prepend pres self
  387.    updateScale self
  388.  
  389.    --*==========================================================================*
  390.    --* Look for a DragController so the tape measure can be dragged around.
  391.    --* If there isn't one, create one.
  392.    --*==========================================================================*        
  393.    if (canObjectDo pres controllersGetter) do
  394.       (
  395.       local controls := pres.controllers
  396.       local dc := chooseOne controls \
  397.                      (
  398.                      control xx ->
  399.                         (
  400.                         if (isAKindOf control DragController) then return true
  401.                                                               else return false
  402.                         )
  403.                      ) undefined
  404.       if (dc != empty) then
  405.          (
  406.          if (NOT dc.wholeSpace) do 
  407.             (
  408.             --*=================================================================*
  409.             --* See explanation for disabling and re-enabling the hook's
  410.             --* DragController in the next comment.
  411.             --*=================================================================*
  412.             self.hook.dc.enabled := false
  413.             append dc self.display
  414.             append dc self
  415.             self.hook.dc.enabled := true
  416.             )
  417.          )
  418.       else
  419.          (
  420.          --*====================================================================*
  421.          --* Ok, there isn't a DragController, so we create one.  Because of a
  422.          --* bug, we have to disable the hook's DragController first and then
  423.          --* re-enable it after appending the tape measure.  If we don't, then
  424.          --* the Window's DragController will override the hook's DragController
  425.          --* and we won't be able to drag the hook independently of the hub.
  426.          --*====================================================================*
  427.          self.hook.dc.enabled := false
  428.          local newDC := new DragController space:pres
  429.          append newDC self.display
  430.          append newDC self
  431.          self.hook.dc.enabled := true
  432.          )
  433.       
  434.       self.foundHome := true
  435.       )
  436.    )
  437.  
  438. in module Scratch
  439. (
  440. --*=============================================================================*
  441. --* Create a library container and save the modules.
  442. --*=============================================================================*
  443. local lc := new LibraryContainer dir:(parentDir theScriptDir) \
  444.                                  path:"tape.sxl"              \
  445.                                  name:"Tape Measure Classes"
  446.  
  447. lc.startupAction := (lc -> forEach lc \
  448.                               (
  449.                               aModule xx -> 
  450.                                  (
  451.                                  load aModule
  452.                                  )
  453.                               ) undefined)
  454.  
  455. append lc (getModule @TapeInterface)
  456. append lc (getModule @TapeImplementation)
  457.  
  458. close lc
  459. )
  460.  
  461. "Compiled tape.sx"
  462.